package cn.wen.javax.xml.oxm;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;

import cn.wen.java.io.HelperOfFile;
import cn.wen.java.io.HelperOfIO;

/**
 * <p>
 * Title: XStream 的 辅助类
 * <p>
 * Description: Object <=> Xml 转化，及Object <=> Json 转化相关操作。
 * <p>
 * Copyright: Copyright (c) 2010-2012
 * 
 * @author <a href="mailto:ct_wen@qq.com">ctwen</a>, 温承瑭
 * @version Revision: 1.0 Date: 2010/12/10 description：初完成。 
 * 	    </P>
 * 	    Revision: 1.5 Date: 2011/05/05 description：主要完善toJson，fromJson功能，新增多个重载方法。
 * 	    </P>
 * 	    Revision: 2.0 Date: 2011/05/06 description：优化类结构。
 * 	    </P>
 * 	    Revision: 3.0 Date: 2012/02/03 description：优化类结构，划分为编组，解组器。
 */
@SuppressWarnings("unchecked")
public class HelperOfXStream {

    private static final Logger log = Logger.getLogger(HelperOfXStream.class);

    private static Marshaller marshaller = null;

    /**
     * @return XStream 的  编组器
     */
    public static Marshaller getMarshaller() {
	if (null == marshaller) {
	    marshaller = new Marshaller();
	}
	return marshaller;
    }
    
    private static Unmarshaller unmarshaller = null;
    
    /**
     * @return XStream 的  解组器
     */
    public static Unmarshaller getUnmarshaller() {
	if (null == unmarshaller) {
	    unmarshaller = new Unmarshaller();
	}
        return unmarshaller;
    }

    /**
     * <p>
     * Title: XStream 的  编组器
     * <p>
     * Description: Object => Xml 编组，及Object => Json 转化相关操作。
     * <p>
     * Copyright: Copyright (c) 2012
     * 
     * @author <a href="mailto:ct_wen@qq.com">ctwen</a>, 温承瑭
     * @version Revision: 1.0 Date: 2012/2/3 description：初完成。 
     * 			</P>
     */
    public static class Marshaller {
	
	/**
	 * 将对象以XML方式编组，并返回XML文本。
	 * 
	 * <PRE>
	 * toXml(UserImpl);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @return XML格式字符串
	 */
	public String toXml(Object obj) {
	    return toText(obj, new XStream());
	}

	/**
	 * 将对象以XML方式编组，保存到指定文件。
	 * 
	 * <PRE>
	 * toXml(UserImpl, file);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @param file 目标文件对象。
	 */
	public void toXml(Object obj, File file) {
	    toXml(obj, file, new XStream());
	}

	// -----------------------------------------------------------------------

	/**
	 * 将对象以XML方式编组，并返回XML文本，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * toXml(UserImpl, classes);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @param classes [数组]类
	 * @return XML格式字符串
	 */
	public String toXml(Object obj, Class<?>... classes) {
	    return setAlias(new XStream(), classes).toXML(obj);
	}

	/**
	 * 将对象以XML方式编组，保存到指定文件，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * toXml(UserImpl, file, classes);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @param classes [数组]类
	 * @param file 目标文件对象。
	 */
	public void toXml(Object obj, File file, Class<?>... classes) {
	    toXml(obj, file, setAlias(new XStream(), classes));
	}

	// -----------------------------------------------------------------------

	/**
	 * 将对象以XML方式编组，并返回XML文本，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * toXml(UserImpl, aliases, classes);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @return XML格式字符串
	 */
	public String toXml(Object obj, String[] aliases, Class<?>[] classes) {
	    return setAlias(new XStream(), aliases, classes).toXML(obj);
	}

	/**
	 * 将对象以XML方式编组，保存到指定文件，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * toXml(UserImpl, file, aliases, classes);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @param file 目标文件对象。
	 */
	public void toXml(Object obj, File file, String[] aliases, Class<?>[] classes) {
	    toXml(obj, file, setAlias(new XStream(), aliases, classes));
	}
	
	/**
	 * 将对象以XML方式编组，保存到指定文件。
	 * 
	 * @param obj 对象
	 * @param file 目标文件对象。
	 * @param xStream XStream对象
	 */
	private void toXml(Object obj, File file, XStream xStream) {
	    // 创建目标目录
	    HelperOfFile.mkdirs(FilenameUtils.getFullPath(file.getAbsolutePath()));

	    OutputStream os = null;
	    try {
		// 文件流
		os = new BufferedOutputStream(new FileOutputStream(file));
		xStream.toXML(obj, os); // 将XML文本写到输出流。
	    }
	    catch (FileNotFoundException e) {
		log.error(e.getMessage(), e);
	    }
	    finally {
		HelperOfIO.closes(os);
	    }
	}
	
	/**
	 * 将对象编组成Json文本。
	 * 
	 * <PRE>
	 * toJson(Json);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @return Json格式字符串
	 */
	public String toJson(Object obj) {
	    return toText(obj, new XStream(new JettisonMappedXmlDriver()));
	}

	// -----------------------------------------------------------------------

	/**
	 * 将对象编组成Json文本，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * toJson(UserImpl, classes);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @param classes [数组]类
	 * @return Json格式字符串
	 */
	public String toJson(Object obj, Class<?>... classes) {
	    return setAlias(new XStream(new JettisonMappedXmlDriver()), classes).toXML(obj);
	}

	// -----------------------------------------------------------------------

	/**
	 * 将对象编组成Json文本，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * toJson(UserImpl, aliases, classes);
	 * </PRE>
	 * 
	 * @param obj 对象
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @return Json格式字符串
	 */
	public String toJson(Object obj, String[] aliases, Class<?>[] classes) {
	    return setAlias(new XStream(new JettisonMappedXmlDriver()), aliases, classes).toXML(obj);
	}
	
    }

    /**
     * <p>
     * Title: XStream 的  解组器
     * <p>
     * Description: Object <= Xml 解组，及Object <= Json 转化相关操作。
     * <p>
     * Copyright: Copyright (c) 2012
     * 
     * @author <a href="mailto:ct_wen@qq.com">ctwen</a>, 温承瑭
     * @version Revision: 1.0 Date: 2012/2/3 description：初完成。 
     * 			</P>
     */
    public static class Unmarshaller {
	
	/**
	 * 将Xml文本解组成对象。
	 * 
	 * <PRE>
	 * fromXml(xml);
	 * </PRE>
	 * 
	 * @param xml Xml文本
	 * @return 装箱成Object类型的对象
	 */
	public Object fromXml(String xml) {
	    return fromText(xml, new XStream());
	}

	/**
	 * 将Xml文本解组成对象
	 * 
	 * <PRE>
	 * fromXml(xml, UserImpl.class);
	 * </PRE>
	 * 
	 * @param <T>
	 * @param xml Xml文本
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromXml(String xml, Class<T> clz) {
	    return (T) fromXml(xml);
	}

	/**
	 * 将Xml文件解组成对象
	 * 
	 * <PRE>
	 * fromXml(file);
	 * </PRE>
	 * 
	 * @param file 文件对象
	 * @return 装箱成Object类型的对象
	 */
	public Object fromXml(File file) {
	    return fromXml(file, new XStream());
	}

	/**
	 * 将Xml文件解组成对象
	 * 
	 * <PRE>
	 * fromXml(file, UserImpl.class);
	 * </PRE>
	 * 
	 * @param <T>
	 * @param file 文件对象
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromXml(File file, Class<T> clz) {
	    return (T) fromXml(file);
	}

	// -----------------------------------------------------------------------

	/**
	 * 将Xml文本解组成对象 ，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromXml(xml, classes);
	 * </PRE>
	 * 
	 * @param xml Xml文本
	 * @param classes [数组]类
	 * @return 装箱成Object类型的对象
	 */
	public Object fromXml(String xml, Class<?>[] classes) {
	    return setAlias(new XStream(), classes).fromXML(xml);
	}

	/**
	 * 将Xml文本解组成对象 ，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromXml(xml, classes, UserImpl.class);
	 * </PRE>
	 * 
	 * @param xml Xml文本
	 * @param classes [数组]类
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromXml(String xml, Class<?>[] classes, Class<T> clz) {
	    return (T) fromXml(xml, classes);
	}

	/**
	 * 将Xml文件解组成对象 ，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromXml(file, classes);
	 * </PRE>
	 * 
	 * @param file 文件对象
	 * @param classes [数组]类
	 * @return 装箱成Object类型的对象
	 */
	public Object fromXml(File file, Class<?>[] classes) {
	    return fromXml(file, setAlias(new XStream(), classes));
	}

	/**
	 * 将Xml文件解组成对象 ，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromXml(file, classes, UserImpl.class);
	 * </PRE>
	 * 
	 * @param <T>
	 * @param file 文件对象
	 * @param classes [数组]类
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromXml(File file, Class<?>[] classes, Class<T> clz) {
	    return (T) fromXml(file, classes);
	}

	// -----------------------------------------------------------------------

	/**
	 * 将Xml文本解组成对象 ，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromXml(xml, aliases, classes);
	 * </PRE>
	 * 
	 * @param xml Xml文本
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @return 装箱成Object类型的对象
	 */
	public Object fromXml(String xml, String[] aliases, Class<?>[] classes) {
	    return setAlias(new XStream(), aliases, classes).fromXML(xml);
	}

	/**
	 * 将Xml文本解组成对象 ，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromXml(xml, aliases, classes, UserImpl.class);
	 * </PRE>
	 * 
	 * @param xml Xml文本
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromXml(String xml, String[] aliases, Class<?>[] classes, Class<T> clz) {
	    return (T) fromXml(xml, aliases, classes);
	}

	/**
	 * 将Xml文件解组成对象 ，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromXml(file, aliases, classes);
	 * </PRE>
	 * 
	 * @param file 文件对象
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @return 装箱成Object类型的对象
	 */
	public Object fromXml(File file, String[] aliases, Class<?>[] classes) {
	    return fromXml(file, setAlias(new XStream(), aliases, classes));
	}

	/**
	 * 将Xml文件解组成对象 ，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromXml(file, aliases, classes, UserImpl.class);
	 * </PRE>
	 * 
	 * @param <T>
	 * @param file 文件对象
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromXml(File file, String[] aliases, Class<?>[] classes, Class<T> clz) {
	    return (T) fromXml(file, aliases, classes);
	}
	
	/**
	 * 将指定文件以Xml方式解组成对象。
	 * 
	 * @param file 文件对象
	 * @param xStream XStream对象
	 * @return 装箱成Object类型的对象
	 */
	private Object fromXml(File file, XStream xStream) {
	    InputStream is = null;
	    try {
		is = new BufferedInputStream(new FileInputStream(file));
		return xStream.fromXML(is);
	    }
	    catch (FileNotFoundException e) {
		log.error(e.getMessage(), e);
	    }
	    finally {
		HelperOfIO.closes(is);
	    }
	    return null;
	}
	
	/**
	 * 将Json格式文本解组成对象。
	 * 
	 * <PRE>
	 * fromJson(Json);
	 * </PRE>
	 * 
	 * @param json Json格式文本
	 * @return 装箱成Object类型的对象
	 */
	public Object fromJson(String json) {
	    return fromText(json, new XStream(new JettisonMappedXmlDriver()));
	}

	/**
	 * 将Json格式文本解组成对象。
	 * 
	 * <PRE>
	 * fromJson(Json, UserImpl.class);
	 * </PRE>
	 * 
	 * @param <T>
	 * @param json Json格式文本
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromJson(String json, Class<T> clz) {
	    return (T) fromJson(json);
	}

	// -----------------------------------------------------------------------

	/**
	 * 将Json格式文本解组成对象，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromJson(json, classes);
	 * </PRE>
	 * 
	 * @param json Json格式文本
	 * @param classes [数组]类
	 * @return 装箱成Object类型的对象
	 */
	public Object fromJson(String json, Class<?>[] classes) {
	    return setAlias(new XStream(new JettisonMappedXmlDriver()), classes).fromXML(json);
	}

	/**
	 * 将Json格式文本解组成对象，可设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字）。
	 * 
	 * <PRE>
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromJson(json, classes, UserImpl.class);
	 * </PRE>
	 * 
	 * @param <T>
	 * @param json Json格式文本
	 * @param classes [数组]类
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromJson(String json, Class<?>[] classes, Class<T> clz) {
	    return (T) fromJson(json, classes);
	}

	// -----------------------------------------------------------------------

	/**
	 * 将Json格式文本解组成对象，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromJson(json, aliases, classes);
	 * </PRE>
	 * 
	 * @param json Json格式文本
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @return 装箱成Object类型的对象
	 */
	public Object fromJson(String json, String[] aliases, Class<?>[] classes) {
	    return setAlias(new XStream(new JettisonMappedXmlDriver()), aliases, classes).fromXML(json);
	}

	/**
	 * 将Json格式文本解组成对象，可设置类型与别名映射。
	 * 
	 * <PRE>
	 * String[] aliases = { &quot;UserImpl&quot; };
	 * Class&lt;?&gt;[] classes = { UserImpl.class };
	 * fromJson(json, aliases, classes, UserImpl.class);
	 * </PRE>
	 * 
	 * @param <T>
	 * @param json Json格式文本
	 * @param aliases [数组]类别名
	 * @param classes [数组]类
	 * @param clz 指定返回值对象的拆箱类型
	 * @return 拆箱成clz类型的对象
	 */
	public <T> T fromJson(String json, String[] aliases, Class<?>[] classes, Class<T> clz) {
	    return (T) fromJson(json, aliases, classes);
	}
	
    }


    // -----------------------------------------------------------------------
    // private member.
    // -----------------------------------------------------------------------

    /**
     * 将对象编组成文本。
     * 
     * @param obj 对象
     * @return 文本
     */
    private static String toText(Object obj, XStream xStream) {
	// xStream.setMode(XStream.NO_REFERENCES);
	return xStream.toXML(obj);
    }

    /**
     * 将文本解组成对象。
     * 
     * @param text 文本
     * @param xStream XStream 对象
     * @return 装箱成Object类型的对象
     */
    private static Object fromText(String text, XStream xStream) {
	// xStream.setMode(XStream.NO_REFERENCES);
	return xStream.fromXML(text);
    }

    /**
     * 设置类型（根据类型的simpleName与别名映射，注：内部会把首字母转小字），并返回。
     * 
     * @param xStream XStream 对象
     * @param classes [数组]类
     * @return XStream 对象
     */
    private static XStream setAlias(XStream xStream, Class<?>[] classes) {
	for (Class<?> clz : classes) {
	    xStream.alias(StringUtils.uncapitalize(clz.getSimpleName()), clz);
	}
	return xStream;
    }

    /**
     * 设置类型与别名映射，并返回。
     * 
     * @param xStream XStream 对象
     * @param aliases [数组]类别名
     * @param classes [数组]类
     * @return XStream 对象
     */
    private static XStream setAlias(XStream xStream, String[] aliases, Class<?>[] classes) {
	for (int i = 0; i < classes.length; i++) {
	    xStream.alias(aliases[i], classes[i]);
	}
	return xStream;
    }

}
